﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows.Threading;

namespace DataGridSelectedItems
{
    /// <summary>
    /// 属性变更基类
    /// </summary>
    public class PropertyChangedBase : INotifyPropertyChanged, IDisposable, INotifyPropertyChanging
    {

        #region 原始


        public event PropertyChangedEventHandler PropertyChanged;

        public event PropertyChangingEventHandler PropertyChanging;

        protected PropertyChangingEventHandler PropertyChangingHandler
        {
            get
            {
                return PropertyChanging;
            }
        }

        protected PropertyChangedBase()
        {
        }

        public void Dispose()
        {
            DoDispose();
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
        }

        public static void InvokeOnUIThread(Action action)
        {
            Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher;
            if (currentDispatcher.CheckAccess())
            {
                action();
            }
            else
            {
                currentDispatcher.BeginInvoke(action, new object[0]);
            }
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if (propertyChanged != null)
            {
                PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
                propertyChanged(this, e);
            }
        }

        #endregion

        public void OnPropertyChanged_Public(string propertyName)
        {
            OnPropertyChanged(propertyName);
        }

        Dictionary<object, object> _ValueDictionary = new Dictionary<object, object>();
        #region 根据属性名得到属性值
        public T GetPropertyValue<T>(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("invalid " + propertyName);
            object _propertyValue;
            if (!_ValueDictionary.TryGetValue(propertyName, out _propertyValue))
            {
                _propertyValue = default(T);
                _ValueDictionary.Add(propertyName, _propertyValue);
            }
            return (T)_propertyValue;
        }
        public void SetPropertyValue<T>(string propertyName, T value)
        {
            if (!_ValueDictionary.ContainsKey(propertyName) || _ValueDictionary[propertyName] != (object)value)
            {
                _ValueDictionary[propertyName] = value;
                OnPropertyChanged(propertyName);
            }
        }
        #endregion

        #region Dispose
        ~PropertyChangedBase()
        {
            Dispose();
        }
        void DoDispose()
        {
            if (_ValueDictionary != null)
                _ValueDictionary.Clear();
        }
        #endregion

        #region Copy from MvvmLight

        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void VerifyPropertyName(string propertyName)
        {
            var myType = GetType();

            if (!string.IsNullOrEmpty(propertyName)
                && myType.GetProperty(propertyName) == null)
            {
                var descriptor = this as ICustomTypeDescriptor;

                if (descriptor != null)
                {
                    if (descriptor.GetProperties()
                        .Cast<PropertyDescriptor>()
                        .Any(property => property.Name == propertyName))
                    {
                        return;
                    }
                }
                throw new ArgumentException("属性值未找到", propertyName);
            }

        }
        public virtual void NotifyOfPropertyChanging([CallerMemberName] string propertyName = null)
        {
            VerifyPropertyName(propertyName);
            var handler = PropertyChanging;
            if (handler != null)
            {
                handler(this, new PropertyChangingEventArgs(propertyName));
            }
        }

        public virtual void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
        {
            VerifyPropertyName(propertyName);
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public virtual void NotifyOfPropertyChanging<T>(Expression<Func<T>> propertyExpression)
        {
            var handler = PropertyChanging;
            if (handler != null)
            {
                var propertyName = GetPropertyName(propertyExpression);
                handler(this, new PropertyChangingEventArgs(propertyName));
            }
        }

        public virtual void NotifyOfPropertyChange<T>(Expression<Func<T>> propertyExpression)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                var propertyName = GetPropertyName(propertyExpression);
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
        {
            if (propertyExpression == null)
            {
                throw new ArgumentNullException("propertyExpression");
            }

            var body = propertyExpression.Body as MemberExpression;

            if (body == null)
            {
                throw new ArgumentException("Invalid argument", "propertyExpression");
            }

            var property = body.Member as PropertyInfo;

            if (property == null)
            {
                throw new ArgumentException("Argument is not a property", "propertyExpression");
            }

            return property.Name;
        }

        protected bool Set<T>(Expression<Func<T>> propertyExpression, ref T field, T newValue)
        {
            if (EqualityComparer<T>.Default.Equals(field, newValue))
            {
                return false;
            }

            NotifyOfPropertyChanging(propertyExpression);
            field = newValue;
            NotifyOfPropertyChange(propertyExpression);
            return true;
        }

        protected bool Set<T>(string propertyName, ref T field, T newValue)
        {
            if (EqualityComparer<T>.Default.Equals(field, newValue))
            {
                return false;
            }

            NotifyOfPropertyChanging(propertyName);
            field = newValue;
            NotifyOfPropertyChange(propertyName);
            return true;
        }

        #endregion

    }

    public static class PropertyNotifyObjectEx
    {
        //得到属性的名称
        public static string GetPropertyName<T, U>(Expression<Func<T, U>> exp)
        {
            string _pName = "";
            if (exp.Body is MemberExpression)
            {
                _pName = (exp.Body as MemberExpression).Member.Name;
            }
            else if (exp.Body is UnaryExpression)
            {
                _pName = ((exp.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
            }
            return _pName;
        }
        public static U GetValue<T, U>(this T t, Expression<Func<T, U>> exp) where T : PropertyChangedBase
        {
            string _pN = GetPropertyName(exp);
            return t.GetPropertyValue<U>(_pN);
        }

        public static void SetValue<T, U>(this T t, Expression<Func<T, U>> exp, U value) where T : PropertyChangedBase
        {
            string _pN = GetPropertyName(exp);
            t.SetPropertyValue<U>(_pN, value);
        }
        public static void OnPropertyChanged<T, U>(this T npb, Expression<Func<T, U>> exp) where T : PropertyChangedBase
        {
            string _PropertyName = GetPropertyName(exp);
            npb.OnPropertyChanged_Public(_PropertyName);
        }
    }
}